//------------------------------------------------------------------------------ 
//  <copyright file="RGBCamera.h" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//  </copyright>
//------------------------------------------------------------------------------
#pragma once

#include "Managed.h"

#include "VideoFormats.h"
#include "CaptureFormat.h"
#include "VideoProperty.h"
#include "ManagedCameraControlProperty.h"
#include "SourceReaderCallback.h"

namespace Microsoft {
    namespace Robotics {
        namespace Vision {
            namespace Cameras {
                namespace Webcam {

    /// <summary>
    // Class used to represent an RGB camera capture device available from Media Foundation.
    // To use the class,
    //   // initialize and connect to camera
    //   RGBCamera.Initialize()  // starts Media Foundation
    //   foreach (var device in RGBCamera.AllDevices)   // cycle through all the available devices
    //            {
    //                if (device.FriendlyName.Equals("mydevicename"))   // check the device's friendly name
    //                if (!device.Attach())   return;                   // attach to device, return if unable to 
    //            }
    //
    //    // grab image, which calls OnImageAvailable	
    //	  ReadSampleDelegate^ myCallback;
    //    myCallback = gcnew ReadSampleDelegate(OnImageAvailable);
    //    rgbCamera->CaptureSample(myCallback);
    //
    //	  // where the callback is given by
    //	  void OnImageAvailable(System::IntPtr buffer, int cbLength) {}
    //
    //	  An example of how to use the camera can be found in RGBCameraConnect.cs
    /// </summary>
    public ref class RGBCamera
    {
    private:
        /// <summary>
        // How many milliseconds in a second
        /// </summary>
        static const int MillisecondsPerSecond = 1000;
        
        /// <summary>
        // Name of the device
        /// </summary>
        String^ m_name;

        /// <summary>
        // Unique identifier for this device
        /// </summary>
        String^ m_symbolicLink;

        /// <summary>
        // The underlying MF Source Reader
        /// </summary>
        IMFSourceReader *m_pSourceReader;

        /// <summary>
        // The underlying MF Source
        /// </summary>
        IMFMediaSource *_pMediaSource;

        /// <summary>
        // The underlying MF Activate
        /// </summary>
        IMFActivate *m_pActivate;

        /// <summary>
        /// The source reader callback which wraps the MF Source Reader
        /// </summary>
        SourceReaderCallback* m_callback;

        /// <summary>
        /// The time the last frame arrived
        /// </summary>
        LONGLONG m_lastFrameTime;

        /// <summary>
        /// Frequency of the performance counter
        /// </summary>
        __int64 m_performanceCounterFrequency;

        /// <summary>
        /// The requested output frame rate numerator
        /// </summary>
        int m_desiredRateNumerator;

        /// <summary>
        /// The requested output frame rate denominator
        /// </summary>
        int m_desiredRateDenominator;

    internal:

        /// <summary>
        /// Private constructor to use with activation
        /// </summary>
        ///<params name="pActivate">The IMFActivate to create a device from</params>
        RGBCamera(IMFActivate *pActivate);

        /// <summary>
        //  Initializes the friendly name and symbolic link of the device from the activate object
        /// </summary>
        /// <param name="pActivate">  The activate object representing the device </param>
        /// <param name="name">  Friendly name </param>
        void InitializeFromActivate(IMFActivate *pActivate, String^ name);

        /// <summary>
        //  Initializes the performance counter frequency
        /// </summary>
        void InitilaizePerformanceCounterFrequency();

        /// <summary>
        /// Uses the symbolic link or device path for a capture device to create an  activate object.
        /// </summary>
        /// <param name="symbolicLink"> Symbolic Link </param>
        IMFActivate *GetActivate(String^ symbolicLink);

        /// <summary>
        /// Handler for read sample completion
        /// </summary>
        ReadSampleDelegate^ m_readSampleCallback;

        /// <summary>
        /// GC Handle for read sample completion
        /// </summary>
        GCHandle^ m_readSampleHandlerHandle;

        /// <summary>
        /// Internal pinned managed Handler for read sample completion
        /// </summary>
        ReadSampleDelegate^ m_readSampleDelegateInternal;

        /// <summary>
        /// Thunks from Managed to unmanaged code
        /// </summary>
        /// <param name="data"> Image data as RGB24 byte array </param>
        /// <param name="cbLength"> Length of Image data in bytes </param>
        void ReadSampleThunk(IntPtr data, int cbLength);

    public:
        /// <summary>
        /// Initializes a new instance of the <c>RGBCamera</c> class.
        /// </summary>
        RGBCamera(String^ name, String^ symbolicLink);

        /// <summary>
        // dtor. Always shut down the capture device.
        /// </summary>
        ~RGBCamera();

        /// <summary>
        ///  This is used to initialize the MediaFoundation library and 
        ///  should be called once before any use of the a MediaFoundation
        ///  capability.
        /// </summary>        
        static void Initialize()
        {
            HRESULT hr = S_OK;

            hr = MFStartup(MF_VERSION, MFSTARTUP_LITE);
            MF_THROWHR(hr);
        }


        /// <summary>
        ///  This is used to uninitialize the MediaFoundation library and 
        ///  should be called once at application shutdown.
        /// </summary>        
        static void Uninitialize()
        {
            HRESULT hr = S_OK;

            hr = MFShutdown();
            MF_THROWHR(hr);
        }

        
        /// <summary>
        ///  Attaches to the underlying device.
        /// </summary>
        ///<returns>True if succesfully attached, false otherwise</returns>
        bool Attach();

        /// <summary>
        ///  Detaches from the underlying device.
        /// </summary>        
        void Shutdown();


        
        /// <summary>
        ///  Sets up the capture pipeline if required and sets up async sample capture
        /// </summary>        
        ///<param name="handler">Handler to call after completing read sample</param>
        void CaptureSample(ReadSampleDelegate^ handler);

        
        /// <summary>
        ///  Sets the value for the specified video property with the given flags
        /// </summary>        
        ///<param name="prop">Property to be set</param>
        ///<param name="nValue">Value of the property</param>
        ///<param name="flags">Flags corresponding to the property</param>
        ///<returns>True if the property was set, false otherwise</returns>
        bool SetProperty(VideoProperty prop, int nValue, VideoPropertyFlags flags);

        /// <summary>
        ///  Gets the value for the specified video property with the given flags
        /// </summary>        
        ///<param name="prop">Property to be set</param>
        ///<param name="nValue">Value of the property</param>
        ///<param name="flags">Flags corresponding to the property</param>
        ///<returns>True if the property was set, false otherwise</returns>
        bool GetProperty(VideoProperty prop, int% nValue, int% flags);

        /// <summary>
        ///  Sets the value for the specified camera property with the given flags
        /// </summary>        
        ///<param name="prop">Property to be set</param>
        ///<param name="nValue">Value of the property</param>
        ///<param name="flags">Flags corresponding to the property</param>
        ///<returns>True if the property was set, false otherwise</returns>
        bool SetProperty(ManagedCameraControlProperty prop, int nValue, ManagedCameraControlPropertyFlags flags);

        /// <summary>
        ///  Gets the value for the specified camera property with the given flags
        /// </summary>        
        ///<param name="prop">Property to be set</param>
        ///<param name="nValue">Value of the property</param>
        ///<param name="flags">Flags corresponding to the property</param>
        ///<returns>True if the property was set, false otherwise</returns>
        bool GetProperty(ManagedCameraControlProperty prop, int% nValue, int% flags);

        /// <summary>
        ///  Gets a list of all available capture devices
        /// </summary>        
        static property IEnumerable<RGBCamera^>^ AllDevices
        {
            IEnumerable<RGBCamera^>^ get();
        }

        /// <summary>
        ///  Gets the friendly name of this device
        /// </summary>        
        property String^ FriendlyName
        {
            String^ get()
            {
                return m_name;
            }
        }

        /// <summary>
        ///  Gets the unique identifier for this device.
        /// </summary>        
        property String^ SymbolicLink
        {
            String^ get()
            {
                return m_symbolicLink;
            }
        }

        /// <summary>
        ///  Gets a boolean indicating if the device is currently attached.
        /// </summary>        
        property bool fAttached
        {
            bool get()
            {
                return m_pSourceReader != NULL;
            }
        }

        /// <summary>
        ///  Gets the list of capture formats supported
        /// </summary> 
        ///<returns>List of supported capture formats</returns>
        property IEnumerable<CaptureFormat^>^ Formats
        {
            IEnumerable<CaptureFormat^>^ get();
        }

        /// <summary>
        ///  Gets the list of video property values supported
        /// </summary>        
        ///<returns>List of supported video property values</returns>
        property IEnumerable<VideoPropertyValue^>^ VideoProperties
        {
            IEnumerable<VideoPropertyValue^>^ get();
        }

        /// <summary>
        ///  Gets the list of camera control property values supported
        /// </summary>        
        ///<returns>List of supported camera control property values</returns>
        property IEnumerable<ManagedCameraControlPropertyValue^>^ ManagedCameraControlProperties
        {
            IEnumerable<ManagedCameraControlPropertyValue^>^ get();
        }

        /// <summary>
        ///  Gets or sets the capture format currently used by the device.
        /// </summary>
        ///<returns>Current capture format</returns>
        property CaptureFormat^ CurrentFormat
        {
            CaptureFormat^ get();
            void set(CaptureFormat^);
        }

    };

}}}}}
